<?php

use Sdk\lib\Format;

error_reporting(E_ERROR);

const WitName = 'WitFrame SDK';
const WitVer = '1.0';
const WitApiURL = 'https://api.witframe.com';
const WitURL = 'https://www.witframe.com';

define('ROOT_PATH', dirname(__FILE__, 2));

const DEFAULT_PATH = '/system/page/index';

class Run {

	private static $_instance;

	var $_appPath;

	public static function getInstance() {
		if (!(self::$_instance instanceof self)) {
			self::$_instance = new self();
		}
		return self::$_instance;
	}

	public function Register() {
		$oldAutoLoader = spl_autoload_functions();
		if ($oldAutoLoader) {
			foreach ($oldAutoLoader as $f) {
				spl_autoload_unregister($f);
			}
		}
		spl_autoload_register(array($this, 'AutoLoader'));

		if (function_exists('__autoload')) {
			spl_autoload_register('__autoload');
		}

		if ($oldAutoLoader) {
			foreach ($oldAutoLoader as $f) {
				spl_autoload_register($f);
			}
		}
	}

	public function Init() {
		$this->_checkEvent();
		$this->Register();
		$this->_callAction();
	}

	public function AutoLoader($class) {
		$e = explode('\\', $class);

		if (count($e) > 1) {
			$f = ROOT_PATH . '/' . str_replace('\\', '/', lcfirst($class)) . '.php';
		} else {
			$p = strpos($class, '_');
			if ($p !== false && substr($class, 0, $p) == 'Model' && defined('PLUGIN_PATH')) {
				$f = PLUGIN_PATH . 'models/' . substr($class, $p + 1) . '.php';
			} elseif ($p !== false && substr($class, 0, $p) == 'Lib' && defined('PLUGIN_ROOT_PATH')) {
				$libClass = substr($class, $p + 1);
				$p = strpos($libClass, '_');
				$ver = substr($libClass, 0, $p);
				$f = PLUGIN_ROOT_PATH . $ver . '/libs/' . substr($libClass, $p + 1) . '.php';
			} else {
				$f = ROOT_PATH . '/framework/' . str_replace('_', '/', $class) . '.php';
			}
		}
		include_once $f;
	}

	public function GetAppPath() {
		return $this->_appPath;
	}

	private function _checkEvent() {
		if (phpversion() < '8.1') {
			die(WitName . ' must run at php 8.1');
		}
		if (!is_writeable(ROOT_PATH . '/conf')) {
			die('Cannot write to path /conf');
		}
		if (!is_writeable(ROOT_PATH . '/www')) {
			die('Cannot write to path /www');
		}
		if (!file_exists(ROOT_PATH . '/tmp')) {
			@mkdir(ROOT_PATH . '/tmp', 0777);
		}
		if (!is_writeable(ROOT_PATH . '/tmp')) {
			die('Cannot write to path /tmp');
		}
	}

	private function _callAction() {
		$path = $_SERVER['PATH_INFO'];
		if (empty($_SERVER['PATH_INFO'])) {
			$path = DEFAULT_PATH;
		}
		$e = explode('/', $path);
		if ($e[1] == 'plugin') {
			if ($e[2] == '') {
				throw new Exception('path is not exists');
			}

			define('PLUGIN_ROOT_PATH', ROOT_PATH . '/www/' . $e[2] . '/');
			$this->_appPath = PLUGIN_ROOT_PATH . $e[3] . '/';
			define('PLUGIN_PATH', $this->_appPath);
			define('PLUGIN_ID', $e[2]);
			define('VER_ID', $e[3]);

			$uri = '/' . implode('/', array_slice($e, 4));
			$rewriteRule = $this->_appPath . '.rewrite';
			if (file_exists($rewriteRule)) {
				$uri = (new Rewrite($rewriteRule))->matchRule($uri);
				@list(, $e[4], $e[5]) = explode('/', $uri);
			}

			$cFile = array();
			foreach (explode('_', $e[4]) as $s) {
				$cFile[] = ucfirst($s);
			}
			$file = ROOT_PATH . '/www/' . $e[2] . '/' . $e[3] . '/controllers/' . implode('/', $cFile) . '.php';
			if (!file_exists($file)) {
				throw new Exception('file is not exists');
			}
			require_once $file;

			$className = implode('_', $cFile) . 'Controller';
			if (!class_exists($className)) {
				throw new Exception('className is not exists');
			}
			$action = !empty($e[5]) ? $e[5] . 'Action' : 'indexAction';
			if (!method_exists($className, $action)) {
				throw new Exception('method is not exists');
			}

			$settingValue = array();
			$settingFile = $this->_appPath . 'conf/setting.php';
			if (file_exists($settingFile)) {
				@include_once $settingFile;
			}
			$GLOBALS['witSetting'] = $settingValue;

			$c = new $className();
			call_user_func_array(array($c, $action), array());
		} elseif ($e[1] == 'system') {
			$script = ucfirst($e[3]);
			$file = ROOT_PATH . '/sdk/' . $e[2] . '/' . $script . '.php';
			if (!file_exists($file)) {
				throw new Exception('file is not exists');
			}
			define('CUR_SCRIPT', $e[2] . $script);

			if (!empty($_GET['appPath'])) {
				list($pluginId, $verId) = explode('.', $_GET['appPath']);
				if (Format::checkAppId($pluginId) && Format::checkBranchId($verId)) {
					define('PLUGIN_ROOT_PATH', ROOT_PATH . '/www/' . $pluginId . '/');
					$this->_appPath = PLUGIN_ROOT_PATH . $verId . '/';
					define('PLUGIN_PATH', $this->_appPath);
					define('PLUGIN_ID', $pluginId);
					define('VER_ID', $verId);
				}
			}

			require_once $file;

			$className = $script;
			if (!class_exists($className)) {
				throw new Exception('className is not exists');
			}
			if (!method_exists($className, 'run')) {
				throw new Exception('method "run" is not exists');
			}

			$this->_appPath = ROOT_PATH . '/sdk/';

			$c = new $className();
			$c->run();
		}
	}
}

class Rewrite {

	var array $rules = array();

	public function __construct($file) {
		foreach (file($file) as $row) {
			if (substr($row, 0, 1) == '#') {
				continue;
			}
			list($search, $replace) = explode(' ', $row);
			$this->rules[trim($search)] = trim($replace);
		}
	}

	public function matchRule($uri) {
		if (!$this->rules) {
			return $uri;
		}
		foreach ($this->rules as $search => $replace) {
			if (preg_match($search, $uri)) {
				$uri = preg_replace($search, $replace, $uri);
				break;
			}
		}

		$e = parse_url($uri);
		if (!empty($e['query'])) {
			parse_str($e['query'], $get);
			$_GET = array_merge($_GET, $get);
		}

		return $e['path'];
	}
}